﻿#ifndef OGLWINDOW_H
#define OGLWINDOW_H

#include<QOpenGLWidget>
#include<QOpenGLFunctions>
#include<QOpenGLVertexArrayObject>
#include<QOpenGLBuffer>
#include<QOpenGLShaderProgram>
#include<QMatrix4x4>
#include<QVector>
#include<QSharedPointer>
#include<glm/glm.hpp>
#include<stack>

#include "global.h"
#include "camera.h"
#include "animator.h"
#include "trackballinteractor.h"
#include "qsmoothdialog.h"

class OglWidget: public QOpenGLWidget,protected QOpenGLFunctions,public llx::Renderer
{
    Q_OBJECT
public:
    OglWidget(QWidget*parent=nullptr,bool nf = false,bool of = false);
    virtual ~OglWidget();

public slots:
    void cleanup();

    void setMenuMode(int);   //set Render MenuBar
    void _loadObject(QString &str);  //read model from file

    void _setInfos();
    void _setAlpha1(double);
    void _setAlpha2(double);
    void _setInteractorSpeed(int);
    void _setCircleOutter(int);
    void _setCircleInner(int);

signals:
    void setVN(const QString&);
    void setFN(const QString&);
    void _setInfoPanel(const QString&); //纯文本
    void enableNeuronGroup(bool);

protected:
    virtual void initializeGL()override;
    virtual void resizeGL(int w,int h)override;
    virtual void paintGL()override;

    virtual void mousePressEvent(QMouseEvent *event)override;
    virtual void mouseReleaseEvent(QMouseEvent *event)override;
    virtual void mouseMoveEvent(QMouseEvent *event)override;
    virtual void mouseDoubleClickEvent(QMouseEvent *event)override;
    virtual void keyPressEvent(QKeyEvent *event)override;
    virtual void keyReleaseEvent(QKeyEvent *event)override;
    virtual void wheelEvent(QWheelEvent *event)override;

    virtual void paintEvent(QPaintEvent *event)override;//rendering 2D and 3D at same time

private:
    void setDefaultObject(); //default model
    void _resetSceneState();

    void _setLightState(bool lightNo,bool lightDiff,bool lightDir,bool lightPGourand,bool lightPPhong, bool lightPBlinnPhong);
    void setGLSL(int); //set light
    void _setMaterial(int); //set material
    bool createShaderProgram(QOpenGLShaderProgram *program, QString path,bool hasGeometry = false); //add, compile and link shader
    void setupVertexAttribs();
    void setVcgMesh(std::vector<unsigned>&vtype1,std::vector<unsigned>&vtype2,std::vector<unsigned>&ftype);

    void _growSpine();  //grow spine for dendrites
    void setSpineMesh();
    void _growMyelinSheath();   //grow myelin sheath for axon
    void setMyelinSheathMesh();

    void _simulateRealSoma();   //real soma deformation
    void _simulateRealSomaAuto();

    void _smoothDendrites();

    void drawScene();

    void _setBaseVertices();  //reference objects' data
    void _setBaseShaderProgram(); //reference objects shader
    void _drawGrid();
    void _drawCenter();
    void _drawCube();
    void _drawBaseObjects();
    void _drawAxes();
    void _drawFrameAxis();  //discard

public:
    QOpenGLVertexArrayObject    m_vao;
    QOpenGLBuffer               m_vbo,m_ebo;
    QOpenGLShaderProgram        *shaderProgram = nullptr;

    QVector<Point3D>m_verts;
    QVector<unsigned>m_indices;
    int  m_vn = 0;
    int  m_fn = 0;
    bool indicesDraw   = true; //默认采用索引绘制
    bool is_Fill_Lines = false; //线框模式显示

    bool _geometry = false;
    vcgMesh _vcgmesh;

    //uniform location in GLSL
    int m_mvMatrixLoc = -1;
    int m_projMatrixLoc = -1;
    int m_normalMatrixLoc = -1;

    int m_lightAmbLoc = -1;
    int m_lightDiffLoc = -1;
    int m_lightSpecLoc = -1;
    int m_lightPosLoc = -1;
    int m_materialAmbLoc = -1;
    int m_materialDiffLoc = -1;
    int m_materialSpecLoc = -1;
    int m_materialShiLoc = -1;
    QVector3D lightPosV = QVector3D(5.0f,2.0f,2.0f);

    QVector<QString>skeletonInfos;//Number of Stems; Number of Bifurcations; Number of Branches; fragmentation; total Length; average diameter

    QSmoothDialog *taubinDlg = nullptr;

private:
    QString shader_path;
    bool newFirst;
    bool openFirst;

    bool _light_DFL = true;
    bool _light_Diffuse = false;
    bool _light_Direction = false;
    bool _light_Point_Gourand = false;
    bool _light_Point_Phong = false;
    bool _light_Point_BlinnPhong = false;

    bool has_materials = false;
    bool neuron = false;    //neuron(true); others(false)
    bool _usePartColor = false; //是否分色显示神经元
    QColor _bgColor = Qt::white;    //scene background color
    QColor _pureSurfaceColor;   //model surface pure color
    float alpha1 = 1.0;
    float alpha2 = 0.5;
    int circleOutter = 1;
    int circleInner = 3;

    bool _drawInfos2d = false;  //draw skeleton infos

    bool _createSpine = false;  //generate spine for dendrites
    vcgMesh *_spinemesh = nullptr;

    bool _createMyelinSheath = false;   //for axon
    QVector<Point3D>axonVerts;
    QVector<unsigned>axonIndices;
    vcgMesh *_axonmesh = nullptr;

    vcgMesh *_somamesh = nullptr;

    //trackball
    llx::Camera llxCamera;
    llx::TrackBallInteractor llxInteractor;
    llx::Animator llxAnimator;

    QMatrix4x4 mMat;
    QMatrix4x4 pMat,vMat;
    std::stack<QMatrix4x4>mvStack; //矩阵堆栈
    QMatrix3x3 m_invTrMat; //法线矩阵

    //basic objects
    GLuint projLoc,mvLoc;
    QOpenGLBuffer gridsVBO;
    QOpenGLBuffer cubeVBO;
    QOpenGLBuffer axesVBO;
    std::vector<glm::vec3>grids;
    std::vector<glm::vec3>cubes; //测试用
    std::vector<glm::vec3>axes;
    std::vector<std::pair<QVector3D,QColor>> _axesLines;
    QOpenGLShaderProgram *gridsProgram = nullptr;
    QOpenGLShaderProgram *cameraProgram = nullptr;
    QOpenGLShaderProgram *cubeProgram = nullptr;
    QOpenGLShaderProgram *axesProgram = nullptr;
};

#endif // OGLWINDOW_H
